#include "DBFile.h"

SortedDBFile::SortedDBFile () {	}

SortedDBFile::~SortedDBFile () { }

int SortedDBFile::Create (char *fpath, fType file_type, void *startup){
    sprintf (filePath, "%s", fpath);
    //create metadata file
	char path[250], c = 's';
	sprintf (path, "%s.header", fpath);
	FILE *metadata = fopen (path, "w");
	fwrite(&c, 1, sizeof(char), metadata);

	orderMaker = ((SortInfo*)startup)->myOrder;
    char *bits = new char[250];
	orderMaker->ToBits(bits);
	fwrite(bits, 1, 250, metadata);

	runLen = ((SortInfo*)startup)->runLength;
	fwrite(&runLen, sizeof(int), 1, metadata);

	fclose(metadata);

	file.Open (0, fpath);
	pageType = reading;
	page = new Page();
	currentPage = 0;

    return 1;
}

int SortedDBFile::Open(char *fpath){
    sprintf (filePath, "%s", fpath);
    char *bits = new char[250];
	char path[250], type;
	sprintf (path, "%s.header", fpath);
	FILE *metadata = fopen(path, "r");
	fread(&type, 1, 1, metadata);

    orderMaker = new OrderMaker;
	fread(bits, 1, 250, metadata);
	orderMaker->FromBits(bits);

	fread(&runLen, sizeof(int), 1, metadata);

    file.Open (1, fpath);
	pageType = reading;
    currentPage = 0;
    page = new Page();
    if(FileLength() > 0)
        file.GetPage(page, 0);
}

int SortedDBFile::Close(){
    SwitchToReading();
    file.Close();
	return 1;
}

void SortedDBFile::Load(Schema &myschema, char *loadme){
    SwitchToWritting();

	FILE *data = fopen(loadme,"r");
	Record *r = new Record;

	while(r->SuckNextRecord(&myschema,data))
		in->Insert(r);

	//in->ShutDown();
	//delete in;
	//in = new Pipe (100);
	fclose(data);
}

void SortedDBFile::MoveFirst(){
    currentPage = 0;
    if(FileLength() > 0)
        file.GetPage (page, 0);
    SwitchToReading();
}

void SortedDBFile::Add(Record &addme){
    SwitchToWritting();
	in->Insert(&addme);
}

int SortedDBFile::GetNext(Record &fetchme){
    SwitchToReading();

	if(!page->GetFirst(&fetchme)){
		currentPage++;
		if(currentPage >= FileLength())
			return 0;

		file.GetPage(page, currentPage);
		page->GetFirst(&fetchme);
	}
	return 1;
}

int SortedDBFile::GetNext(Record &fetchme, CNF &cnf, Record &literal){
    SwitchToReading();
    ComparisonEngine e;

    if (!queryOrderMakerCreated && !queryOrderMakerFailed){
        if (cnf.CreateQueryOrderMaker(*orderMaker, queryOrderMaker, literalOrderMaker)){
            queryOrderMakerCreated = true;
            queryOrderMakerFailed = false;
            BinarySearch(fetchme, literal);
        }
        else{
            queryOrderMakerFailed = true;
            queryOrderMakerCreated = false;
        }
    }

    while (GetNext(fetchme)){
        if (e.Compare(&fetchme,&literal,&cnf)){
            foundMatch = true;
            return 1;
        }
        else if (queryOrderMakerCreated && foundMatch)
            return 0;
    }

    return 0;
}

void SortedDBFile::Merge(){
    Record recordFromFile;
    Record recordFromPipe;
    ComparisonEngine e;
    Page writtingPage;

    char tempPath[250];
	sprintf(tempPath, "%s.tmp", filePath);

	File mergeFile;
	mergeFile.Open(0, tempPath);
    MoveFirst();

    bool p = out->Remove(&recordFromPipe);
    bool f = GetNext(recordFromFile);
    int r = 0;

	while(true){
	    if (p && f){
	        r = e.Compare(&recordFromPipe, &recordFromFile, orderMaker);
	        if (r < 0)
	            AddToFile(mergeFile, writtingPage, recordFromPipe);
	        else
	            AddToFile(mergeFile, writtingPage, recordFromFile);
	    }

	    else if (f){
	        AddToFile(mergeFile, writtingPage, recordFromFile);
	        r = 0;
	    }

	    else if (p){
	        AddToFile(mergeFile, writtingPage, recordFromPipe);
	        r = -1;
	    }

        else { break; }

        if (r < 0)
            p = out->Remove(&recordFromPipe);
        else
            f = GetNext(recordFromFile);
	}

    int i = 0;
    if(!mergeFile.GetLength())
            i = 0;
    else
        i = mergeFile.GetLength() - 1;

    mergeFile.AddPage(&writtingPage, i);
    mergeFile.Close();
    file.Close();
    remove(filePath);
    rename(tempPath,filePath);
    file.Open(1, filePath);
}

void SortedDBFile::SwitchToReading(){
    if (pageType == writing){
		pageType = reading;
		in->ShutDown();
		Merge();
		delete bigQ;
		delete in;
		delete out;
	}
}

void SortedDBFile::SwitchToWritting(){
    if (pageType == reading){
        queryOrderMakerCreated = false;
        queryOrderMakerFailed = false;
        foundMatch = false;
		pageType = writing;
		in = new Pipe (100);
		out = new Pipe (100);
		bigQ = new BigQ( *in, *out, *orderMaker, runLen);
	}
}

void SortedDBFile::AddToFile(File& mergeFile, Page &writtingPage, Record &record){
    if(!writtingPage.Append(&record))
	{
	    int i = 0;
	    if(!mergeFile.GetLength())
                i = 0;
        else
            i = mergeFile.GetLength() - 1;

		mergeFile.AddPage(&writtingPage, i);
		writtingPage.EmptyItOut();
		writtingPage.Append(&record);
	}
}

void SortedDBFile::BinarySearch(Record &fetchme, Record &literal){
	ComparisonEngine e;
	int start = 0, end = FileLength()- 1, middle, r;

	while (end - start > 1){
        middle = ((start + end) / 2);

        file.GetPage(page, middle);
		page->GetFirst(&fetchme);

		r = e.Compare(&fetchme, &queryOrderMaker, &literal, &literalOrderMaker);

		if(r >= 0)
			end = middle;
		else
			start = middle;
	}

    file.GetPage(page, start);
    currentPage = start;
}






